import bcrypt from "bcryptjs"
import { v2 as cloudinary } from "cloudinary"

// models
import Notification from "../models/notification.model.js"
import User from "../models/user.model.js"

export const getUserProfile = async (req, res) => {
    // NOTE: pass username param
    const { username } = req.params

    try {
        const user = await User.findOne({ username }).select("-password")
        if (!user) {
            return res.status(404).json({
                message: "User not found"
            })
        }

        res.status(200).json(user)
    } catch (error) {
        console.log("Error in getUserProfile ", error.message)
        res.status(500).json({
            error: error.message
        })
    }
}

export const followUnfollowUser = async (req, res) => {
    try {
        const { id } = req.params
        const userToModify = await User.findById(id)
        const currentUser = await User.findById(req.user._id)
        // self user
        if (id === req.user._id.toString()) {
            return res.status(400).json({
                error: "You can not follow or unfollow yourself"
            })
        }

        const isFollowing = currentUser.following.includes(id)

        if (isFollowing) {
            // me Unfollow the user
            await User.findByAndUpdate(id, { $pull: { followers: req.user._id } })
            await User.findByIdAndUpdate(req.user._id, { $pull: { following: id } })

            res.status(200).json({
                message: "User unfollowed successfully"
            })
        } else {
            // me Follow the user
            await User.findByIdAndUpdate(id, { $push: { followers: req.user._id } })
            await User.findByIdAndUpdate(req.user._id, { $push: { following: id } })
            // Send notification to the user
            const newNotification = new Notification({
                type: "follow",
                from: req.user._id,
                to: userToModify._id,
            })

            await newNotification.save()

            res.status(200).json({
                message: "User followed successfully"
            })
        }
    } catch (error) {
        console.log("Error in followUnfollowUser ", error.message)
        return res.status(500).json({
            error: error.message
        })
    }
}

export const getSuggestedUsers = async (req, res) => {
    try {
        const userId = req.user._id

        const usersFollowedByMe = await User.findById(userId).select("following")

        const users = await User.aggregate([
            {
                $match: {
                    _id: { $ne: userId }, // not equal to userId
                },
            },
            { $sample: { size: 10 } },
        ])

        const filteredUsers = users.filter((user) => {
            !usersFollowedByMe.following.includes(user._id)
        })

        const suggestedUsers = filteredUsers.slice(0, 4)
        // NOTE: why unset the password
        suggestedUsers.forEach((user) => {
            user.password = null
        })

        res.status(200).json(suggestedUsers)
    } catch (error) {
        // TODO: console.log format
        console.log("Error in getSuggestedUsers: ", error.message)
        res.status(500).json({
            error: error.message
        })
    }
}

export const updateUser = async (req, res) => {
    const { fullName, email, username, currentPassword, newPassword, bio, link } = req.body

    let { profileImg, coverImg } = req.body

    const userId = req.user._id

    try {
        // try update password
        let user = await User.findById(userId)
        if (!user) {
            return res.status(404).json({
                message: "User not found"
            })
        }

        if (
            (!newPassword && currentPassword) ||
            (!currentPassword && newPassword)
        ) {
            return res.status(400).json({
                error: "Please provide both current password and new password"
            })
        }

        if (currentPassword && newPassword) {
            const isMatch = await bcrypt.compare(currentPassword, user.password)
            if (!isMatch) {
                return res.status(400).json({
                    error: "Current password is incorrect"
                })
            }
            // password len error
            if (newPassword.length < 6) {
                return res.status(400).json({
                    error: "Password must be at least 6 characters long"
                })
            }
            // hash password
            const salt = await bcrypt.genSalt(10)
            user.password = await bcrypt.hash(newPassword, salt)
        }
        // try update profileImg
        if (profileImg) {
            if (user.profileImg) {
                // REF LINK:
                // https://res.cloudinary.com/dyfqon1v6/image/upload/v1712997552/zmxorcxexpdbh8r0bkjb.png
                await cloudinary.uploader.destroy(
                    user.profileImg.split("/").pop().split(".")[0]
                )
            }

            const uploadedResponse = await cloudinary.uploader.upload(profileImg)
            profileImg = uploadedResponse.secure_url
        }

        // try update coverImg
        if (coverImg) {
            if (user.coverImg) {
                await cloudinary.uploader.destroy(
                    user.coverImg.split("/").pop().split(".")[0]
                )
            }

            const uploadedResponse = await cloudinary.uploader.upload(coverImg)
            coverImg = uploadedResponse.secure_url
        }

        // update other field when req.body passing
        user.fullName = fullName || user.fullName
        user.email = email || user.email
        user.username = username || user.username
        user.bio = bio || user.bio
        user.link = link || user.link
        user.profileImg = profileImg || user.profileImg
        user.coverImg = coverImg || user.coverImg

        user = await user.save()

        // password should be null in response, because I dont want someone else get
        // the password
        user.password = null

        return res.status(200).json(user)
    } catch (error) {
        console.log("Error in updateUser: ", error.message)
        res.status(500).json({
            error: error.message
        })
    }
}